package com.beordie.version_2.mvc;

import com.beordie.annotation.*;
import com.beordie.version_2.context.BODApplicationContext;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.logging.Handler;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Description
 * @Date 2021/12/28 14:14
 * @Created 30500
 */
public class DispatchServlet extends HttpServlet {

    /**
     * url 和 方法的对应关系
     */
    private List<BODHandlerMapping> handlerMappings = new ArrayList<>();

    /**
     * 映射器和适配器的关系
     */
    private Map<BODHandlerMapping, BODHandlerAdapter> handlerAdapters = new HashMap<>();

    /**
     * 视图关系
     */
    private List<BODViewResolver> viewResolvers = new ArrayList<>();
    /**
     * 上下文对象
     */
    private BODApplicationContext applicationContext = null;
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        try {
            doDispatch(req, resp);
        } catch (Exception e) {
            Map<String, Object> result = new HashMap<>();
            result.put("details", Arrays.toString(e.getStackTrace()));
            processDispatchResult(req, resp, new BODModelAndView("500", result));
        }
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException, InvocationTargetException, IllegalAccessException {
        BODHandlerMapping handler = getHandler(req);
        if (handler == null) {
            processDispatchResult(req, resp, new BODModelAndView("404"));
            return;
        }

        BODHandlerAdapter adapter = getHandlerAdapter(handler);

        BODModelAndView view = adapter.handle(req, resp, handler);

        processDispatchResult(req, resp, view);

    }

    private BODHandlerAdapter getHandlerAdapter(BODHandlerMapping handler) {
        if (this.handlerAdapters.size() == 0) return null;
        return this.handlerAdapters.get(handler);
    }

    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, BODModelAndView bodModelAndView) {
        if (bodModelAndView == null || this.viewResolvers.size() == 0) { return; }
        for (BODViewResolver viewResolver : this.viewResolvers) {
            BODView view = viewResolver.resolverViewName(bodModelAndView.getViewName());
            try {
                view.render(bodModelAndView.getModel(), req, resp);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 正则, 获取 handler Mapping 对象
     * @param req
     * @return
     */
    private BODHandlerMapping getHandler(HttpServletRequest req) {
        String uri = req.getRequestURI();
        String path = req.getContextPath();
        String url = uri.replace(path, "").replaceAll("/+", "/");
        for (BODHandlerMapping handlerMapping : this.handlerMappings) {
            Matcher matcher = handlerMapping.getUrl().matcher(url);
            if (!matcher.matches()) { continue; }
            return handlerMapping;
        }
        return null;
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        this.applicationContext = new BODApplicationContext(config.getInitParameter("config-name"));

        //  建立 url 映射
        initStrategies(this.applicationContext);

        System.out.println("初始化数据完成");
    }

    /**
     * 初始化处理组件
     * @param context
     */
    protected void initStrategies(BODApplicationContext context) {
        //多文件上传的组件
        //initMultipartResolver(context);
        //初始化本地语言环境
        //initLocaleResolver(context);
        //初始化模板处理器
        //initThemeResolver(context);
        //handlerMapping
        initHandlerMappings(context);
        //初始化参数适配器
        initHandlerAdapters(context);
        //初始化异常拦截器
        //initHandlerExceptionResolvers(context);
        //初始化视图预处理器
        //initRequestToViewNameTranslator(context);
        //初始化视图转换器
        initViewResolvers(context);
        //FlashMap管理器
        //initFlashMapManager(context);
    }

    /**
     *
     * @param context
     */
    private void initHandlerAdapters(BODApplicationContext context) {
        if (this.handlerMappings.size() == 0) { return; }
        for (BODHandlerMapping mapping : this.handlerMappings) {
            this.handlerAdapters.put(mapping, new BODHandlerAdapter());
        }
    }

    /**
     * 初始化模板引擎
     * @param context
     */
    private void initViewResolvers(BODApplicationContext context) {
        String templateRoot = context.getConfig("template");
        this.viewResolvers.add(new BODViewResolver(templateRoot));
    }

    /**
     * 初始化映射关系
     * @param context
     */
    private void initHandlerMappings(BODApplicationContext context) {
        if (this.applicationContext.getBeanDefinitionCount() == 0) { return ; }
        for (String beanName : this.applicationContext.getBeanDefinitionNames()) {
            Object bean = this.applicationContext.getBean(beanName);
            Class<?> aClass = bean.getClass();
            if (!aClass.isAnnotationPresent(BODController.class)) { continue; }
            String baseUrl = null;
            if (aClass.isAnnotationPresent(BODRequestMapping.class)) {
                baseUrl = aClass.getAnnotation(BODRequestMapping.class).value();
            }
            for (Method method : aClass.getMethods()) {
                if (!method.isAnnotationPresent(BODRequestMapping.class)) { continue; }
                BODRequestMapping annotation = method.getAnnotation(BODRequestMapping.class);
                String url = ("/" + baseUrl + "/" + annotation.value())
                        .replaceAll("//*", ".*")
                        .replaceAll("/+", "/");
                Pattern pattern = Pattern.compile(url);
                this.handlerMappings.add(new BODHandlerMapping(method, bean, pattern));
            }
        }
    }

}
